package com.google.agera;

import ohos.eventhandler.EventHandler;
import ohos.eventhandler.EventRunner;
import ohos.eventhandler.InnerEvent;

import java.lang.ref.WeakReference;

/**
 * Shared per-thread worker Handler behind internal logic of various Agera classes.
 */
final class WorkerHandler extends EventHandler {
    static final int MSG_FIRST_ADDED = 0;
    static final int MSG_LAST_REMOVED = 1;
    static final int MSG_UPDATE = 2;
    static final int MSG_CALL_UPDATABLE = 3;
    static final int MSG_CALL_MAYBE_START_FLOW = 4;
    static final int MSG_CALL_ACKNOWLEDGE_CANCEL = 5;
    private static final ThreadLocal<WeakReference<WorkerHandler>> handlers = new ThreadLocal<>();

    @NonNull
    private final IdentityMultimap<Updatable, Object> scheduledUpdatables;

    @NonNull
    static WorkerHandler workerHandler() {
        final WeakReference<WorkerHandler> handlerReference = handlers.get();
        WorkerHandler handler = handlerReference != null ? handlerReference.get() : null;
        if (handler == null) {
            handler = new WorkerHandler(EventRunner.getMainEventRunner());
            handlers.set(new WeakReference<>(handler));
        }
        return handler;
    }

    private WorkerHandler(EventRunner runner) {
        super(runner);
        this.scheduledUpdatables = new IdentityMultimap<>();
    }

    synchronized void removeUpdatable(@NonNull final Updatable updatable, @NonNull final Object token) {
        scheduledUpdatables.removeKeyValuePair(updatable, token);
    }

    synchronized void update(@NonNull final Updatable updatable, @NonNull final Object token) {
        if (scheduledUpdatables.addKeyValuePair(updatable, token)) {
            sendEvent(InnerEvent.get(WorkerHandler.MSG_CALL_UPDATABLE, updatable));
        }
    }

    @Override
    protected void processEvent(InnerEvent event) {
        super.processEvent(event);
        switch (event.eventId) {
            case MSG_UPDATE:
                ((BaseObservable) event.object).sendUpdate();
                break;
            case MSG_FIRST_ADDED:
                ((BaseObservable) event.object).observableActivated();
                break;
            case MSG_LAST_REMOVED:
                ((BaseObservable) event.object).observableDeactivated();
                break;
            case MSG_CALL_UPDATABLE:
                final Updatable updatable = (Updatable) event.object;
                if (scheduledUpdatables.removeKey(updatable)) {
                    updatable.update();
                }
                break;
            case MSG_CALL_MAYBE_START_FLOW:
                ((CompiledRepository) event.object).maybeStartFlow();
                break;
            case MSG_CALL_ACKNOWLEDGE_CANCEL:
                ((CompiledRepository) event.object).acknowledgeCancel();
                break;
            default:
        }
    }
}
